feat(events): replaces notifications gate with Events API polling#82
Merged
wgordon17 merged 7 commits intogordon-code:mainfrom Apr 24, 2026
Merged
Conversation
- Add events.ts with fetchUserEvents(), parseRepoEvents(), ETag management - Remove notifications gate: hasNotificationChanges, skipped field, background tab gating, notification 403 handler, POLL_MANAGED_SOURCES entry - Add createEventsPollCoordinator with 60s setTimeout chain, dual race guards - Add fetchTargetedRepoData with MAX_TARGETED_REPOS=10 cap (SEC-IMPL-003) - Add seedHotSetsFromTargeted for additive hot set population - Wire targeted merge in DashboardPage with ID-based dedup, surfacedBy union - Delete poll-notification-effects.test.ts (all tests were notification gate)
- Remove notifications OAuth scope from oauth.ts - Remove notifications scope UI from LoginPage.tsx - Replace notifications API source with userEvents in api-usage.ts - Update DEPLOY.md, README.md, USER_GUIDE.md notification references - Update test mocks and assertions for the source rename
- Captures cache snapshot eagerly before setTimeout (CR-001/PERF-001) - Adds per-repo 2-min cooldown to prevent API amplification (PERF-004) - Adds parts.length guard in fetchTargetedRepoData (SEC-004) - Wires resetEventsState into resetPollState for test isolation (CR-010) - Removes stale notification references from README and USER_GUIDE - Fixes ApiUsageSection test mock for notifications→userEvents rename
STRUCT-012: fetchTargetedRepoData now returns empty data immediately when cooldown filtering removes all repos, avoiding unnecessary GraphQL/REST calls and no-op merge pipeline execution.
- events.test.ts: 22 tests for fetchUserEvents (ETag, 304, first-call, numeric ID dedup, empty username guard) and parseRepoEvents (filtering, flags, case-insensitive matching, timestamps) - events-poll.test.ts: 22 tests for fetchTargetedRepoData (scoped repos, workflow activity filter, cooldown, cap, malformed names), seedHotSetsFromTargeted (pending guard, enriched guard, additive only, generation preservation), and config-change effects
- Tracked-user item preservation across targeted refresh - surfacedBy annotation union merging - detectNewItems + dispatchNotifications called after merge - seedHotSetsFromTargeted called (not rebuildHotSets) - MCP relay exclusion (lastRefreshedAt unchanged)
- Extract handleTargetedData to named module-scope function - Fix fetchUserEvents changed flag on empty first call - Reset consecutiveFailures on non-error skip paths - Add isFullRefreshing re-check after fetchTargetedRepoData - Add user login reactive effect to reset events ETag state - Remove redundant lowercase Set copy in parseRepoEvents - Rename trackedRepoNames to getTrackedRepoNames - Fix indentation in pollFetch if/else block - Replace plan-tag comments with descriptive WHY comments - Add EVENTS_POLL_INTERVAL_MS explanatory comment - Strengthen surfacedBy union tests with value assertions - Add 13 new tests covering untested code paths
wgordon17
added a commit
to wgordon17/github-tracker
that referenced
this pull request
Apr 30, 2026
Merges upstream/main bringing in: - feat(events): replaces notifications gate with Events API polling (gordon-code#82) - fix(dashboard): removes stale merged/closed PRs (gordon-code#83) Conflict in DashboardPage.test.tsx resolved by removing two skipped-fetch tests (notifications gate concept replaced by events API). WAF smoke test hardened with retry logic and sequential execution.
wgordon17
added a commit
that referenced
this pull request
Apr 30, 2026
* feat(jira): adds Jira Cloud integration
- Jira OAuth 3LO auth with API token fallback via Worker proxy
- RYO Jira client (~200 LOC): IJiraClient interface, JiraClient (OAuth/Bearer),
JiraProxyClient (sealed API token through Worker)
- Worker endpoints: token exchange, refresh, API proxy, accessible-resources
- Jira auth store with single-flight refresh, cross-tab sync, triple-guard
- Issue key detection (JIRA_KEY_REGEX) with inline badges on GitHub items
- Jira Assigned tab with JQL search, project grouping, filters, pagination
- Jira issue bookmarking to Tracked tab with absence-based auto-prune
- Settings page: OAuth connect, API token connect, disconnect, key detection toggle
- CSP connect-src updated for api.atlassian.com
- 19 security constraints enforced (sealed tokens, UUID cloudId, endpoint allowlist,
no-secret logging, generic error responses, separate rate limiter)
* fix(jira): addresses review findings from Phase 4 + 4.5
- fixes Jira tab unreachable from stale custom tab redirect (CR-001)
- adds redirect:error to JiraClient fetch calls (SEC-001/002)
- fixes broken siteUrl in API token mode (QA-001)
- adds null check after ensureJiraTokenValid in getAccessToken (STRUCT-001)
- wires onResealed callback for SEAL_KEY rotation (STRUCT-002/CR-003)
- adds .catch on key detection promise (STRUCT-002-conc)
- clears jira key cache on auth change (STRUCT-003/008)
- extracts jiraStatusCategoryClass to shared util (UI-001/QA-005)
- caps jira key cache at 500 entries (PERF-001)
- returns cache copy not reference (PERF-002)
- adds cf-turnstile-response to CORS Allow-Headers (API-001)
- separates refresh rate limiter from exchange (API-002)
- adds aria-label to tab list (UI-007)
- fixes migration paths for jiraAssigned key (CR-002/QA-002)
* docs(jira): adds Jira integration setup and usage documentation
- USER_GUIDE.md: Jira Cloud Integration section (OAuth, API token, key detection, tabs, bookmarking, troubleshooting)
- CONTRIBUTING.md: Jira env vars for development setup
- README.md: Jira integration feature entry
* fix(jira): resolves all remaining review findings and test gaps
- fixes 9 code quality issues (UI-003/004/006/008/009/012, PERF-004/008, STRUCT-007)
- resolves 10 needs-input findings (API-003/004/005/006/007/008, SEC-003, CR-009, PERF-005, QA-008)
- creates 3 missing test files (jira-keys, JiraBadge, JiraAssignedTab) — 37 new tests
- fixes 17 skipped tests in JiraSection.test.tsx and JiraCallback.test.tsx
- adds JiraAuthStateSchema Zod validation for localStorage parse
- adds payload_too_large ErrorCode, input length caps, bulkfetch item cap
- adds loading spinner, title attributes, type=button, aria headings
- caches HKDF derived key for proxy re-seal performance
* test(jira): adds missing Jira tracked item and tab filter tests
- adds setTabFilter('jiraAssigned') runtime verification tests
- adds Jira tracked item tests: trackItem dedup by jiraKey, untrackJiraItem,
moveJiraItem, mixed GitHub+Jira coexistence, source migration default
- removes inconsistent VITE_JIRA_CLIENT_ID default from vitest.config.ts
* test(jira): adds TrackedTab Jira component tests and docs
- adds 7 TrackedTab component tests: Jira item rendering with key/title/status,
htmlUrl linking, mixed GitHub+Jira coexistence, unpin, number-prefix guard
- adds USER_GUIDE troubleshooting entries for API token 403 and multi-tab refresh
- fixes setEnv helper to properly test undefined vs empty string cases
* fix(jira): address 23 PR review findings
- fix: X-Requested-With header changed to 'fetch' in 3 client call sites
- fix: ensureJiraTokenValid guards against missing/invalid expires_in
- security: add validateOrigin to Jira OAuth token exchange and refresh
- security: add email length cap (254 chars) in proxy handler
- security: remove Worker accessible-resources endpoint (access token stays client-side)
- security: add Zod validation for Atlassian accessible-resources response
- perf: lookupKeys returns only requested keys, not full cache copy
- perf: extract pinnedJiraKeys Set memo replacing per-row O(N) scan
- refactor: JiraBadge uses Show accessor form, TypeBadge adds jiraIssue arm
- refactor: use Jira issue.id for tracked item id, unexport JIRA_KEY_REGEX
- style: remove redundant clearJiraKeyCache, fix misleading comments, IIFE
- test: add coverage for onResealed, issueIdsOrKeys cap, SEAL_KEY_NEXT reseal
- test: add jiraStatusCategoryClass done/ghost, pin/unpin, Zod validation
* fix(jira): harden expires_in guard in callback against undefined/NaN
Math.max(undefined, 60) returns NaN — align with auth.ts typeof guard.
* fix(jira): adds Zod validation for token exchange
- Zod schema validates token exchange response
- CORS for POST bulkfetch confirmed working
- Updates jira-keys.ts fallback comment
- Adds test for wrong-shape auth eviction
* fix(jira): address PR review findings from code review cycle
- Guard array spread on proxy reseal to prevent response corruption
- Add per-element string validation for issueIdsOrKeys in proxy
- Add params allowlists for search and issue proxy endpoints
- Unify handleProxySeal to use getJiraEncryptKey (SEAL_KEY_NEXT)
- Add Zod safeParse to cross-tab Jira auth storage sync
- Fix auto-prune to collect keys before mutating store array
- Add jiraAssigned to BUILTIN_TAB_IDS, remove 5 manual carve-outs
- Add key-match verification in JiraProxyClient.getIssue
- Replace per-key evictIfAtCap with batch evictToFit
- Add cloudId to settings export, hoist regex constants
- Fix zod/v4 import, jiraEnabled accessor, dead null check
- Add 8 tests: navigation, expires_in edges, filters, email boundary
* fix(jira): local dev fixes and error observability
- Jira section always visible in Settings (API token works without OAuth client ID)
- Auto-discover Cloud ID from site URL via /api/jira/tenant-info endpoint
- Subdomain + domain picker replaces full URL input (https:// prefix hardcoded)
- Fix siteUrl not stored in config (caused localhost links)
- Fix Undefined priority pill (filter out Jira's Undefined priority name)
- Fix redirect: error unsupported in workerd (8 occurrences across worker)
- Fix Turnstile ready() incompatible with dynamic script loading
- Fix Turnstile size: invisible no longer valid (use compact)
- Fix Turnstile action-mismatch with test keys (skip check when action absent)
- Add Sentry.captureException to 6 silent catch blocks across auth pages and poll
- Surface actual error messages in Jira connect catch block
- Update docs and tests for all changes
* fix(jira): stabilize tab data and add sort/density/collapse
- Move jiraIssues, jiraLoading, jiraKeyMap signals to module level so
they persist across DashboardPage remounts (e.g., Settings navigation)
- Add onMount Jira fetch to fill data immediately on mount
- Add reactive cleanup effect when Jira auth is cleared
- Add catch-all notification for non-JiraApiError errors
- Clear Jira state in onAuthCleared handler
- Add SortDropdown with priority, status, key, updated options
- Add compact: CSS density variants matching GitHub tabs
- Add per-project expand/collapse via expandedRepos store
- Add project pinning via RepoLockControls with locked-first ordering
- Add updated to DEFAULT_FIELDS for sort-by-updated support
- Default project groups to expanded (collapse on user action)
* fix(jira): address quality gate review findings
- Add isSafeJiraSiteUrl guard for href/htmlUrl (CWE-601)
- Move sort signals to module level for tab-switch persistence
- Fix project toggle using setAllExpanded (toggleExpandedRepo
was no-op on first click with default-expanded state)
- Remove phantom empty locked groups from pagination
- Use </> instead of localeCompare for ISO 8601 date sort
* fix(jira): address Layer 2 adversarial review findings
- Add single-flight guard to fetchJiraAssigned (prevents race
between onMount fetch and deferred poll effect)
- Match isExpanded to codebase pattern (!! coercion, default
collapsed) so collapse toggle works correctly
- Add updated field to JiraIssueFields type for sort safety
- Update test mock with Proxy for expanded-by-default in tests
* fix(jira): moves fetch guard to module scope
Prevents remount from creating a parallel unguarded fetch
when the previous mount's fetchJiraAssigned is still in-flight.
* fix(jira): adds siteUrl guard to JiraBadge, auto-expands groups
- Guards JiraBadge href with isSafeJiraSiteUrl (same CWE-601 fix
as JiraAssignedTab)
- Auto-expands all project groups on first render when no prior
expand/collapse state exists in localStorage
* fix(jira): adds tests for new features, hardens sort lookup
- Uses null-prototype objects for PRIORITY_ORDER and
STATUS_CATEGORY_ORDER (prevents prototype key collision)
- Adds updated field to test fixtures in both test files
- Adds 8 new tests: priority sort ordering, sort dropdown
rendering, expand/collapse header + toggle + bulk buttons,
compact density hides assignee, siteUrl XSS guard
* fix(jira): uses group-aware pagination, natural key sort, test reset
- Replaces flat pagination with groupByRepo + computePageLayout +
slicePageGroups pipeline (matches IssuesTab/PullRequestsTab pattern,
groups never split across pages)
- Natural sort for Jira keys: PROJ-9 before PROJ-10 (splits on
dash, compares project prefix then issue number)
- Exports _resetJiraTabState for test isolation of module-level
sort signals (called in beforeEach)
- Updates pagination test to use multi-project layout
* fix(jira): backfills siteUrl from auth, normalizes priority names
- Backfills config.jira.siteUrl from jiraAuth on mount when missing
(fixes localhost links for configs saved before siteUrl field existed)
- Strips parenthesized suffixes from priority names in display badge
and sort comparator (e.g., 'Low (migrated)' displays as 'Low' and
sorts correctly with Low priority)
* fix(jira): removes redundant assignee, inlines compact title
- Removes assignee display from Jira Assigned tab (always the
current user per JQL assignee = currentUser())
- Compact mode: title renders as inline span in the key+badges
flex row, wrapping naturally instead of forcing a second line
- Comfortable mode: title remains as block <p> below key row
* feat(jira): renders issue type icons with hover tooltips
- Adds issuetype to DEFAULT_FIELDS and JiraIssueFields type
- Renders Atlassian-hosted issue type icon (16x16) before key
- Native title attribute shows type name on hover (e.g., Story,
Bug, Epic, Task)
* fix(jira): uses Tooltip component for issue type hover
Replaces native title attribute with the project's Tooltip
component, matching the pattern used across all other tabs.
* fix(jira): adds text fallback when issue type icon is missing
Shows type name as ghost badge when iconUrl is absent (e.g.,
migrated or custom issue types without icons).
* chore(jira): adds dev-only diagnostic for missing issuetype
* fix(jira): falls back to text badge when icon blocked by client
Handles ad blockers or content filters that block specific icon
URLs (e.g., epic.svg) via img onerror → text badge fallback.
* feat(jira): adds inline SVG fallback icons for common issue types
Maps Epic (purple lightning), Story (green card), Task (blue check),
Bug (red circle), Subtask to inline SVGs. Falls back to text badge
for unrecognized types. Handles ad-blocked icon URLs gracefully.
* chore(jira): adds dev-only key detection diagnostic
* chore(jira): adds guard-level diagnostic for key detection
* fix(jira): removes defer from key detection effect
The deferred effect never fires when titleFingerprint has already
settled before the effect registers (cached data + identical poll
results). Without defer, the effect runs immediately on mount when
data exists, and the guards handle the no-data case.
* feat(jira): adds source tooltip to JiraBadge (title vs branch)
- JiraBadge accepts optional source prop ('title', 'branch',
'title & branch') shown in Tooltip on hover
- PullRequestsTab annotates each detected key with its source
(title, branch, or both)
- IssuesTab passes source='title' (issues have no branch ref)
- Replaces native title attribute with Tooltip component
* fix(jira): improves JiraBadge tooltip with summary and source prefix
Tooltip now shows key+status, issue summary, and 'from: title'
or 'from: branch' on separate lines.
* fix(jira): refines JiraBadge tooltip content
Drops redundant key (already in badge), shows status + summary +
source. Source only shown when differentiating (not for 'title &
branch'). Uses 'discovered from PR title/branch' phrasing.
* fix(jira): adds priority sort type with correct labels
Extends SortOption type with 'priority' variant. Shows
'(highest first)' / '(lowest first)' instead of the nonsensical
'(fewest)' / '(most)' that the generic number type produced.
* feat(jira): improves sort options and moves badges right
Restructures issue row layout to place status and priority badges
on the right side, keeping titles left-aligned. Adds preferredDirection
to SortOption for controlling dropdown list order. Fixes status sort
to use SDLC order (To Do → In Progress → Done). Adds created and
title sort fields. Requests created field from Jira API.
* feat(jira): adds SDLC sub-ordering for indeterminate statuses
Adds STATUS_SDLC_ORDER map derived from Red Hat MGMT project workflows
for fine-grained sorting within the indeterminate status category.
Statuses progress: Assigned → In Progress → Code Review → Dev Complete
→ QA → Approved → Ready for Release → Blocked. Unknown statuses fall
back to alphabetical ordering in the middle of the progression.
* fix(jira): changes default sort to Status (To Do → Done)
* test(jira): temporarily switches JQL to reporter for UAT
* feat(jira): adds scope dropdown for assigned/created/watching
Adds scope filter to Jira tab toolbar with three modes: Assigned to me,
Created by me, and Watching. Scope changes trigger a re-fetch with the
appropriate JQL (assignee/reporter/watcher). Clears issue list on scope
switch to show loading state. Reverts temporary reporter-only JQL.
* fix(jira): pins filter toolbar with sticky positioning
* revert(jira): removes broken sticky toolbar positioning
* fix(jira): address PR review findings — security, correctness, tests
- Align isSafeJiraSiteUrl with server-side ATLASSIAN_HOST_RE regex
- Add validateProxyRequest to all Jira Worker endpoints (token exchange, refresh, tenant-info)
- Replace scope-dependent auto-prune with targeted bulkFetch status check
- Add auth guard to prevent cross-user data race in fetchJiraAssigned
- Reset _jiraFetching in onAuthCleared to prevent permanent fetch blocking
- Move Jira sort state from module-level signals to viewState store
- Apply stripParenthetical normalization to priority filter
- Use filtered count for Jira tab badge (matching other tabs)
- Strip resealed field from JiraProxyClient.searchJql return
- Remove custom domain option from Settings (Jira Cloud uses *.atlassian.net)
- Remove dead Content-Type checks (now handled by validateProxyRequest)
- Extract stripParenthetical to shared format.ts
- Add tests: isSafeJiraSiteUrl, buildJiraAuthorizeUrl, getIssue key-mismatch,
ensureJiraTokenValid, handleJiraTenantInfo, X-Requested-With validation
* fix(jira): adds auth guard test, explicit array join
- Test cross-user data race: verifies fetchJiraAssigned does not write
stale issues when auth is cleared mid-flight
- Replace implicit String(array) coercion with explicit Array.isArray
check and .join(',') in Worker proxy search params
* chore: merges upstream/main into jira branch
Merges upstream/main bringing in:
- feat(events): replaces notifications gate with Events API polling (#82)
- fix(dashboard): removes stale merged/closed PRs (#83)
Conflict in DashboardPage.test.tsx resolved by removing two
skipped-fetch tests (notifications gate concept replaced by events API).
WAF smoke test hardened with retry logic and sequential execution.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary